{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a734a988-5048-4d34-85cd-1728edd8b3ed",
   "metadata": {},
   "source": [
    "# 1 Build a Basic Chatbot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "id": "2a0c7cc9-2325-404c-abff-75312162c309",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from langchain_ollama import ChatOllama\n",
    "llm = ChatOllama(base_url=\"http://192.168.99.142:11434\", model=\"hhao/qwen2.5-coder-tools:latest\")\n",
    "\n",
    "from typing import Annotated\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.graph.message import add_messages\n",
    "\n",
    "class State(TypedDict):\n",
    "    # Messages have the type \"list\". The `add_messages` function\n",
    "    # in the annotation defines how this state key should be updated\n",
    "    # (in this case, it appends messages to the list, rather than overwriting them)\n",
    "    messages: Annotated[list, add_messages]\n",
    "\n",
    "graph_builder = StateGraph(State)\n",
    "\n",
    "\n",
    "def chatbot(state: State):\n",
    "    return {\"messages\": [llm.invoke(state[\"messages\"])]}\n",
    "\n",
    "\n",
    "# The first argument is the unique node name\n",
    "# The second argument is the function or object that will be called whenever\n",
    "# the node is used.\n",
    "graph_builder.add_node(\"chatbot\", chatbot)\n",
    "graph_builder.add_edge(START, \"chatbot\")\n",
    "graph_builder.add_edge(\"chatbot\", END)\n",
    "graph = graph_builder.compile()\n",
    "\n",
    "from IPython.display import Image, display\n",
    "\n",
    "try:\n",
    "    display(Image(graph.get_graph().draw_mermaid_png()))\n",
    "except Exception:\n",
    "    # This requires some extra dependencies and is optional\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "34ae2625-0085-4f2c-a258-ced332f6a7f7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "user: Hi, I am hawk.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "Hi, I am hawk.\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Hello Hawk! How can I assist you today?\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "user: how many people in China?\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "how many people in China?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "As of 2023, the estimated population of China is approximately 1.4 billion people. This figure can vary slightly depending on the source and the specific time period being considered. For the most accurate and up-to-date information, you might want to refer to official government statistics or reputable demographic studies.\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "user: do you know my name?\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "do you know my name?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "No, I don't have any information about your personal details unless you provide it explicitly in our conversation. My focus is on assisting with coding tasks and providing technical support based on the information you share. If you need help with a specific programming problem or task, feel free to describe it, and I'll do my best to assist you!\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "user: q\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Goodbye!\n"
     ]
    }
   ],
   "source": [
    "while True:\n",
    "    query = input(\"user:\")\n",
    "    if query.lower() in [\"quit\", \"exit\", \"q\"]:\n",
    "            print(\"Goodbye!\")\n",
    "            break\n",
    "    for chunk in graph.stream(\n",
    "        {\"messages\": [(\"human\", query)]},\n",
    "        stream_mode=\"values\",\n",
    "    ):\n",
    "        chunk[\"messages\"][-1].pretty_print()\n",
    "        "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0524ce36-ab42-4962-be07-adc9723a164a",
   "metadata": {},
   "source": [
    "## 2 Enhancing the Chatbot with Tools"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "6efaf27c-79e4-499d-8daf-dc20b650daf5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[StructuredTool(name='multiply', description='multi first_int and second_int', args_schema=<class 'langchain_core.utils.pydantic.multiply'>, func=<function multiply at 0x000002168542C7C0>), StructuredTool(name='add', description='add first_int and second_int', args_schema=<class 'langchain_core.utils.pydantic.add'>, func=<function add at 0x000002168542F060>), StructuredTool(name='get_weather', description='Call to get the current weather.', args_schema=<class 'langchain_core.utils.pydantic.get_weather'>, func=<function get_weather at 0x000002168542DEE0>), StructuredTool(name='get_coolest_cities', description='Get a list of coolest cities', args_schema=<class 'langchain_core.utils.pydantic.get_coolest_cities'>, func=<function get_coolest_cities at 0x000002168542C540>)]\n"
     ]
    }
   ],
   "source": [
    "from langgraph.prebuilt import ToolNode, tools_condition\n",
    "from langchain_core.tools import tool\n",
    "# 定义函数并应用工具装饰器\n",
    "@tool\n",
    "def multiply(first_int: int, second_int: int) -> int:\n",
    "    \"\"\"multi first_int and second_int\"\"\"\n",
    "    return first_int * second_int\n",
    "\n",
    "@tool\n",
    "def add(first_int: int, second_int: int) -> int:\n",
    "    \"\"\"add first_int and second_int\"\"\"\n",
    "    print(\"\\n add function\")\n",
    "    return first_int + second_int\n",
    "@tool\n",
    "def get_weather(location: str):\n",
    "    \"\"\"Call to get the current weather.\"\"\"\n",
    "    if location.lower() in [\"sf\", \"san francisco\"]:\n",
    "        return \"It's 60 degrees and foggy.\"\n",
    "    else:\n",
    "        return \"It's 90 degrees and sunny.\"\n",
    "\n",
    "\n",
    "@tool\n",
    "def get_coolest_cities():\n",
    "    \"\"\"Get a list of coolest cities\"\"\"\n",
    "    return \"nyc, sf\"\n",
    "tools = [multiply, add, get_weather, get_coolest_cities]\n",
    "print(tools)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "4f8de9e3-2f07-431b-a975-650a70b460d6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from typing import Annotated\n",
    "from langchain_ollama import ChatOllama\n",
    "llm = ChatOllama(base_url=\"http://192.168.99.142:11434\", model=\"hhao/qwen2.5-coder-tools:latest\")\n",
    "\n",
    "from langchain_core.messages import BaseMessage\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.graph.message import add_messages\n",
    "from langgraph.prebuilt import ToolNode, tools_condition\n",
    "\n",
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "memory = MemorySaver()\n",
    "\n",
    "class State(TypedDict):\n",
    "    messages: Annotated[list, add_messages]\n",
    "\n",
    "\n",
    "graph_builder = StateGraph(State)\n",
    "\n",
    "llm_with_tools = llm.bind_tools(tools)\n",
    "\n",
    "\n",
    "def chatbot(state: State):\n",
    "    return {\"messages\": [llm_with_tools.invoke(state[\"messages\"])]}\n",
    "\n",
    "\n",
    "graph_builder.add_node(\"chatbot\", chatbot)\n",
    "\n",
    "tool_node = ToolNode(tools)\n",
    "graph_builder.add_node(\"tools\", tool_node)\n",
    "\n",
    "graph_builder.add_conditional_edges(\n",
    "    \"chatbot\",\n",
    "    tools_condition,\n",
    ")\n",
    "# Any time a tool is called, we return to the chatbot to decide the next step\n",
    "graph_builder.add_edge(\"tools\", \"chatbot\")\n",
    "graph_builder.add_edge(START, \"chatbot\")\n",
    "\n",
    "graph = graph_builder.compile()\n",
    "from IPython.display import Image, display\n",
    "try:\n",
    "    display(Image(graph.get_graph().draw_mermaid_png()))\n",
    "except Exception:\n",
    "    # This requires some extra dependencies and is optional\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "84330a9a-c270-477e-a4d9-85b202d98fd1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "user: what is 23 + 45 ?\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "what is 23 + 45 ?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  add (b6375bf3-d939-465b-92c1-7f1ca68bb888)\n",
      " Call ID: b6375bf3-d939-465b-92c1-7f1ca68bb888\n",
      "  Args:\n",
      "    first_int: 23\n",
      "    second_int: 45\n",
      "\n",
      " add function\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: add\n",
      "\n",
      "68\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "The result of 23 + 45 is 68.\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "user: what's the weather in sf?\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "what's the weather in sf?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  get_weather (b613a1f4-ac7b-4e19-9114-2454c93ef11b)\n",
      " Call ID: b613a1f4-ac7b-4e19-9114-2454c93ef11b\n",
      "  Args:\n",
      "    location: San Francisco\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: get_weather\n",
      "\n",
      "It's 60 degrees and foggy.\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "The current weather in San Francisco is 60 degrees with fog. Is there anything else you'd like to know or discuss about this information?\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "user: q\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Goodbye!\n"
     ]
    }
   ],
   "source": [
    "while True:\n",
    "    query = input(\"user:\")\n",
    "    if query.lower() in [\"quit\", \"exit\", \"q\"]:\n",
    "            print(\"Goodbye!\")\n",
    "            break\n",
    "    for chunk in graph.stream(\n",
    "        {\"messages\": [(\"human\", query)]},\n",
    "        stream_mode=\"values\",\n",
    "    ):\n",
    "        chunk[\"messages\"][-1].pretty_print()\n",
    "        "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3492856e-2944-4065-8564-6fd20170d8d3",
   "metadata": {},
   "source": [
    "## 3 add memmory"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "ec4d5433-080e-4fa9-bae8-6cf2d3e00aa6",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "memory = MemorySaver()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "369e91fc-57b7-4fcf-90b1-c53388cfb4f8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from langchain_ollama import ChatOllama\n",
    "llm = ChatOllama(base_url=\"http://192.168.99.142:11434\", model=\"hhao/qwen2.5-coder-tools:latest\")\n",
    "\n",
    "from typing import Annotated\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.graph.message import add_messages\n",
    "\n",
    "class State(TypedDict):\n",
    "    # Messages have the type \"list\". The `add_messages` function\n",
    "    # in the annotation defines how this state key should be updated\n",
    "    # (in this case, it appends messages to the list, rather than overwriting them)\n",
    "    messages: Annotated[list, add_messages]\n",
    "\n",
    "graph_builder = StateGraph(State)\n",
    "\n",
    "\n",
    "def chatbot(state: State):\n",
    "    return {\"messages\": [llm.invoke(state[\"messages\"])]}\n",
    "\n",
    "\n",
    "# The first argument is the unique node name\n",
    "# The second argument is the function or object that will be called whenever\n",
    "# the node is used.\n",
    "graph_builder.add_node(\"chatbot\", chatbot)\n",
    "graph_builder.add_edge(START, \"chatbot\")\n",
    "graph_builder.add_edge(\"chatbot\", END)\n",
    "#graph = graph_builder.compile()\n",
    "graph = graph_builder.compile(checkpointer=memory)\n",
    "\n",
    "from IPython.display import Image, display\n",
    "\n",
    "try:\n",
    "    display(Image(graph.get_graph().draw_mermaid_png()))\n",
    "except Exception:\n",
    "    # This requires some extra dependencies and is optional\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "840ce350-3324-411c-8ed1-8a21dfc2a996",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "user: hi, I am hawk.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "hi, I am hawk.\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Hello Hawk! How can I assist you today?\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "user: do you know my name?\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "do you know my name?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Yes, I know your name is Hawk. Is there something specific you would like assistance with?\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "user: how many people in China?\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "how many people in China?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "As of 2023, the estimated population of China is approximately 1.4 billion people. For the most current and accurate data, you might want to check official sources or recent reports.\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "user: do you know my name?\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "do you know my name?\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "\n",
      "Yes, I know your name is Hawk. How can I assist you today?\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "user: q\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Goodbye!\n"
     ]
    }
   ],
   "source": [
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "while True:\n",
    "    query = input(\"user:\")\n",
    "    if query.lower() in [\"quit\", \"exit\", \"q\"]:\n",
    "            print(\"Goodbye!\")\n",
    "            break\n",
    "    for chunk in graph.stream(\n",
    "        {\"messages\": [(\"human\", query)]},\n",
    "        config,\n",
    "        stream_mode=\"values\",\n",
    "    ):\n",
    "        chunk[\"messages\"][-1].pretty_print()\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8bea6809-17e1-439b-84e9-3e2dbcb220f9",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "984003d1-a0dc-49a9-90f4-2ce60f479f67",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
